/* 
 * File:   SyncCommand.cpp
 * Author: Bram
 * 
 * Created on January 23, 2013, 2:42 AM
 */


#include "SyncCommand.h"
#include "Config.h"

SyncCommand SyncCommand::cInstance("SYNC");

SyncCommand::SyncCommand(const char* szID )
	: Command( szID )
{
}

SyncCommand::SyncCommand() : Command() {
    setName("SYNC");
}

SyncCommand::SyncCommand(const SyncCommand& orig) {
}

SyncCommand::~SyncCommand() {
}

Command* SyncCommand::createNew()
{
    SyncCommand * cmd = new SyncCommand();
    cmd->setName("SYNC");
    return cmd;
}

bool SyncCommand::isValid()
{
    return ((getName().compare("SYNC") == 0) && (sizeParameters() > 1) && Command::isValid());
}

//hier ontvangt de server het commando van de client
void SyncCommand::ExcecuteServerReceiveCommand(TcpSocket* dasServer, queue<Command *>& commandQueue)
{
    cout << getName() << " COMMAND RECEIVED!" << endl;
    dasLog << getName() << " COMMAND RECEIVED!" << endl;
    
}
//hier komt de server nadat hij een bericht van de client heeft ontvangen met een info request
void SyncCommand::ExcecuteServerSendCommand(TcpSocket* dasServer, queue<Command *>& commandQueue)
{
    setType("END");
    addParameter(Config::getInstance()->getServerName());
    addParameter(Config::getInstance()->getServerVersion());
    addParameter(Config::getInstance()->getServerAbout());
    
    //checkt of het command valide is
    if(!isValid()){
        clearParameters();
        stringstream ssERROR;
        ssERROR << "ERROR 3005 - ERROR BUILDING FULL COMMAND - NOT VALID: ";
        ssERROR << "name: '" << getName()  << "', ";
        ssERROR << "sender: '" << getSender()  << "', ";
        ssERROR << "type: '" << getType()  << "', ";
        addParameter(ssERROR.str());
        //bouwt eerst de error message en past daarna pas de message aan!
        setName("ERROR");
        setType("END");
    }
    string totalCommandStr = buildFullCommand();
    if(totalCommandStr.size() > 10){
        dasServer->sendMessage(totalCommandStr);
        cout << "Server send successfully '" << getName() << "' message to client!" << endl;
        dasLog << "Server send successfully '" << getName() << "' message to client!" << endl;
    } else {
        cout << "ERROR in '" << __FILE__ << "' - '" << __FUNCTION__  << "()', failled on: if(totalCommandStr.size() > 10){ - should not ever happen! 88882" << endl;
        dasLog << "ERROR in '" << __FILE__ << "' - '" << __FUNCTION__  << "()', failled on: if(totalCommandStr.size() > 10){ - should not ever happen! 88882" << endl;
    }
    
}

//dit wordt uitgevoerd als de client het naar de server HEEFT VERSTUURD 
//en gaat wachten op een antwoord!
void SyncCommand::ExcecuteClientReceiveCommand(TcpSocket* dasClient, queue<Command *>& commandQueue)
{
    if(!(getName().compare("ERROR") == 0) ){ //als het geen error is
        //haalt het volgende bericht op!
        //dit bericht is een DIR result!
        Command * receivedCMD = getNextCommandFromSocket(dasClient);
        if(receivedCMD != 0){ //kan eigelijk nooit 0 zijn, wordt afgevangen!
            //als het GEEN error is
            if( !(receivedCMD->getName().compare("ERROR") == 0) ){ //als het geen error is
                //als het commando gelijk is aan het huidige commando!
                if(receivedCMD->getName().compare("DIR") == 0){
                    string clientDir;
                    string fullClienFoldertPath;
                    string clientBasePath;
                    string pathSEP = Config::getInstance()->PathSeparator;
                    string serverDir;
                    vector<string> ffsOnServer;
                    vector<string> ffsOnClient;
                    vector<string> ffsToDeleteOnServer;
                    vector<string> ffsToSendToServer;
                    vector<string> ffsToGetFromServer;
                    getParameterAtIndex(0,clientDir);
                    getParameterAtIndex(1,serverDir);
                    Config::getInstance()->getFullBasePath(clientBasePath);

                    stringstream ss; // client folder to sync locatie
                    ss << clientBasePath << clientDir << pathSEP;
                    fullClienFoldertPath = ss.str();
                    //cout << "fullClienFoldertPath: " << fullClienFoldertPath << endl;
                    //cout << "clientBasePath: " << clientBasePath << endl;
                    Utility::getInstance()->getFilesInDirIncludingSubdirs(fullClienFoldertPath,ffsOnClient, true); // krijgt alle files en folders van de client

                    string firstParam;
                    receivedCMD->getParameterAtIndex(0,firstParam);
                    if(firstParam.compare(Config::getInstance()->OkRepresenter) == 0) {
                        //loopt door alle files die op de server staan
                        //de 1e parameter geeft aan of alles goedgegaan is!
                        for(int i = 1; i < receivedCMD->sizeParameters(); i++){
                            string ff;
                            receivedCMD->getParameterAtIndex(i,ff);
                            Utility::getInstance()->fixPathLinuxWindowsProblem(ff, false);
                            ffsOnServer.push_back(ff);
                            //cout << "received from server: '" << ff << "'" << endl;
                        }
                    }

                    //bepaalt welke bestanden van de server verwijderd moeten worden
                    for(int j = 0; j < ffsOnServer.size(); j++){
                        vector<string> splittedShizlS = Utility::getInstance()->split(ffsOnServer[j], '|');
                        string ffServer = splittedShizlS[0];
                        int ffServerTimestamp = atoi(splittedShizlS[1].c_str());
                        bool isOnClient = false;

                        for(int k = 0; k < ffsOnClient.size(); k++){
                            vector<string> splittedShizlC = Utility::getInstance()->split(ffsOnClient[k], '|');
                            string ffClient = splittedShizlC[0];
                            int ffClientTimestamp = atoi(splittedShizlC[1].c_str());
                            
                            if(ffServer.compare(ffClient) == 0){
                                isOnClient = true;
                                break;
                            }
                        }
                        //als het bestand niet op de client  staat en WEL op de server
                        //dan moet ie dus van de server afgehaald worden
                        if(!isOnClient){
                            stringstream ffSS;
                            ffSS << serverDir << pathSEP << ffServer;
                            ffsToDeleteOnServer.push_back(ffSS.str());
                        }
                    }

                    //bepaalt welke bestanden naar de server toe moeten worden verzonden
                    for(int l = 0; l < ffsOnClient.size(); l++){
                        vector<string> splittedShizlC = Utility::getInstance()->split(ffsOnClient[l], '|');
                        string ffClient = splittedShizlC[0];
                        int ffClientTimestamp = atoi(splittedShizlC[1].c_str());
                        //cout << "ffsOnClient[" << l << "]: " << ffClient << endl;
                        bool isOnServer = false;

                        for(int k = 0; k < ffsOnServer.size(); k++){
                            vector<string> splittedShizlS = Utility::getInstance()->split(ffsOnServer[k], '|');
                            string ffServer = splittedShizlS[0];
                            int ffServerTimestamp = atoi(splittedShizlS[1].c_str());
                            if(ffClient.compare(ffServer) == 0){
                                if(ffServerTimestamp > ffClientTimestamp){ // die op de server is nieuwer, moet dus geGET wordens
                                    string dasPathOnServer = ffServer;
                                    bool isPath = Utility::getInstance()->FileToPath(dasPathOnServer, true);
                                    stringstream ffSS;
                                    ffSS << serverDir << pathSEP << ffClient << "|" << clientDir;
                                    if(isPath){
                                        ffSS << pathSEP << dasPathOnServer;
                                    }
                                    ffsToGetFromServer.push_back(ffSS.str());
                                    isOnServer = true;
                                } else if(ffServerTimestamp < ffClientTimestamp){ //die op de server is ouder dan die op de client
                                    //isOnServer = false;
                                } else { //if(ffServerTimestamp == ffClientTimestamp){ //moet niks mee gedaan worden, zijn gelijk aan elkaar
                                    isOnServer = true;
                                }
                                
                                break;
                            }
                        }
                        //als het bestand niet op de server staat en WEL op de client
                        //dan moet ie dus naar de server gepusht worden
                        if(!isOnServer){
                            stringstream ffSS;
                            ffSS << clientDir << pathSEP << ffClient;
                            ffsToSendToServer.push_back(ffSS.str());
                        }
                    }

                    //als er bestanden gewist moeten worden op de server, bouwt hij die command en voegt hem toe aan de queue
                    if(ffsToDeleteOnServer.size() > 0){
                        Command * delCMD = Command::Generate("DEL");
                        delCMD->setSender("CLIENT");
                        delCMD->setType("END");
                        for(int h = 0; h < ffsToDeleteOnServer.size(); h++){
                            string ffToDelSrv = ffsToDeleteOnServer[h];
                            //cout << "ffToDelSrv: " << ffToDelSrv << endl;
                            delCMD->addParameter(ffToDelSrv);
                        }
                        commandQueue.push(delCMD);
                    }

                    if(ffsToSendToServer.size() > 0){
                        for(int h = 0; h < ffsToSendToServer.size(); h++){
                            string ffPutSrv = ffsToSendToServer[h];
                            //cout << "ffPutSrv: " << ffPutSrv << endl;
                            Command * putCMD = Command::Generate("PUT");
                            putCMD->setSender("CLIENT");
                            putCMD->setType("START");
                            putCMD->addParameter(ffPutSrv); // file locatie van de client
                            string locationToPutFileOnServ = ffPutSrv;

                            Utility::getInstance()->FileToPath(locationToPutFileOnServ, true); //haalt de filename eraf
                            //cout << "locationToPutFileOnServ: " << locationToPutFileOnServ << endl;
                            int size1 = serverDir.size();
                            int size2 = locationToPutFileOnServ.size();
                            locationToPutFileOnServ = ((clientDir.size() == locationToPutFileOnServ.size()) ? "" : locationToPutFileOnServ.substr(clientDir.size() + 1));
                            //cout << "locationToPutFileOnServ: " << locationToPutFileOnServ << endl;
                            stringstream ss1; ss1 << serverDir << pathSEP << locationToPutFileOnServ; locationToPutFileOnServ = ss1.str();
                            //cout << "locationToPutFileOnServ: " << locationToPutFileOnServ << endl;

                            putCMD->addParameter(locationToPutFileOnServ); // folder locatie op de server
                            commandQueue.push(putCMD);
                        }
                    }
                    
                    
                    if(ffsToGetFromServer.size() > 0){
                        for(int h = 0; h < ffsToGetFromServer.size(); h++){
                            vector<string> splittedShizlS = Utility::getInstance()->split(ffsToGetFromServer[h], '|');
                            
                            string ffgetSrv = splittedShizlS[0];
                            string ffgetPutClient = splittedShizlS[1];
                            //cout << "ffPutSrv: " << ffPutSrv << endl;
                            Command * getCMD = Command::Generate("GET");
                            getCMD->setSender("CLIENT");
                            getCMD->setType("START");
                            getCMD->addParameter(ffgetSrv); // file locatie op de server
                            getCMD->addParameter(ffgetPutClient); // file locatie op de server
                            commandQueue.push(getCMD);
                            /*
                            string locationToPutFileOnServ = ffgetSrv;

                            Utility::getInstance()->FileToPath(locationToPutFileOnServ, true); //haalt de filename eraf
                            //cout << "locationToPutFileOnServ: " << locationToPutFileOnServ << endl;
                            int size1 = serverDir.size();
                            int size2 = locationToPutFileOnServ.size();
                            locationToPutFileOnServ = ((clientDir.size() == locationToPutFileOnServ.size()) ? "" : locationToPutFileOnServ.substr(clientDir.size() + 1));
                            //cout << "locationToPutFileOnServ: " << locationToPutFileOnServ << endl;
                            stringstream ss1; ss1 << serverDir << pathSEP << locationToPutFileOnServ; locationToPutFileOnServ = ss1.str();
                            //cout << "locationToPutFileOnServ: " << locationToPutFileOnServ << endl;

                            putCMD->addParameter(locationToPutFileOnServ); // folder locatie op de server
                            commandQueue.push(putCMD);
                             */
                        }
                    }
                    
                } else{
                    cout << "ERROR in '" << __FILE__ << "' - '" << __FUNCTION__  << "()', curr cmd: '" << getName() << "', received cmd: '" << receivedCMD->getName() << "' - should not ever happen! 88886" << endl;
                    dasLog << "ERROR in '" << __FILE__ << "' - '" << __FUNCTION__  << "()', curr cmd: '" << getName() << "', received cmd: '" << receivedCMD->getName() << "' - should not ever happen! 88886" << endl;
                }
            } else { //error
                stringstream ssERROR;
                for(int i = 0; i < receivedCMD->sizeParameters(); i++){
                    string strErr;
                    receivedCMD->getParameterAtIndex(0,strErr);
                    ssERROR << strErr << " - ";
                }
                string strTotalErr = ssERROR.str();
                cout << "INFO COMMAND FAILLED! received error: " << strTotalErr << endl;
                dasLog << "INFO COMMAND FAILLED! received error: " << strTotalErr << endl;
            }
        }

        receivedCMD = 0;
        delete receivedCMD;
    }
}

//dit wordt uitgevoerd als de client het naar de server verstuurd
void SyncCommand::ExcecuteClientSendCommand(TcpSocket* dasClient, queue<Command *>& commandQueue)
{
    //we gaan syncen, we willen eerst weten welke bestanden er in de te syncen directory staat op de server!
    setType("END");
    
    string clientDir;
    string serverDir;
    getParameterAtIndex(0,clientDir);
    getParameterAtIndex(1,serverDir);
    
    //checkt of het command valide is
    if(!(isValid() || (clientDir.size() > 0) || (serverDir.size() > 0)) ){
        clearParameters();
        stringstream ssERROR;
        ssERROR << "ERROR on sending '" << getName() << "' command to server! Command is nog valid! - ";
        ssERROR << "ERROR 3006 - ERROR BUILDING FULL COMMAND - NOT VALID: ";
        ssERROR << "name: '" << getName()  << "', ";
        ssERROR << "sender: '" << getSender()  << "', ";
        ssERROR << "type: '" << getType()  << "', ";
        cout << ssERROR.str() << endl;
        dasLog << ssERROR.str() << endl;
        
        clearParameters();
        addParameter(ssERROR.str());
        setName("ERROR");
        //@TODO huidigge command error van maken, dat komt dan naar voren in de client receive
    } else { //als alles goed is
        //[dir:"test" "1" "1"] OF [dir:"test" "FULL" "RELATIVE"] = alle files in de relatieve dir op de server, MET subdirs
        Command * dirCMD = Command::Generate("DIR");
        dirCMD->setSender("CLIENT");
        dirCMD->setType("END");
        dirCMD->addParameter(serverDir);
        dirCMD->addParameter("1"); //recursief
        dirCMD->addParameter("1"); // relatieve path
        dirCMD->addParameter("1"); //timestamp last edited file
        
        string totalCommandStr = dirCMD->buildFullCommand();
        if(totalCommandStr.size() > 10){
            dasClient->sendMessage(totalCommandStr);
        } else {
            stringstream ss;
            ss << "ERROR in '" << __FILE__ << "' - '" << __FUNCTION__  << "()', if(totalCommandStr.size() > 10){ - should not ever happen! 88887" << endl;
            
            cout << ss.str();
            dasLog << ss.str();
            
            addParameter(ss.str());
            setName("ERROR");
            //@TODO huidigge command error van maken, dat komt dan naar voren in de client receive
        }
        delete dirCMD;
    }
}